home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / sbin / update-rc.d < prev    next >
Text File  |  2009-10-19  |  16KB  |  575 lines

  1. #! /usr/bin/perl
  2. #
  3. # update-rc.d    Update the links in /etc/rc[0-9S].d/
  4. #
  5.  
  6. use strict;
  7. use warnings;
  8.  
  9. my $initd = "/etc/init.d";
  10. my $etcd  = "/etc/rc";
  11. my $notreally = 0;
  12.  
  13. # Print usage message and die.
  14.  
  15. sub usage {
  16.     print STDERR "update-rc.d: error: @_\n" if ($#_ >= 0);
  17.     print STDERR <<EOF;
  18. usage: update-rc.d [-n] [-f] <basename> remove
  19.        update-rc.d [-n] <basename> defaults [NN | SS KK]
  20.        update-rc.d [-n] <basename> start|stop NN runlvl [runlvl] [...] .
  21.        update-rc.d [-n] <basename> disable|enable [S|2|3|4|5]
  22.         -n: not really
  23.         -f: force
  24.  
  25. The disable|enable API is not stable and might change in the future.
  26. EOF
  27.     exit (1);
  28. }
  29.  
  30. # Dependency based boot sequencing is the default, but upgraded
  31. # systems might keep the legacy ordering until the sysadm choose to
  32. # migrate to the new ordering method.
  33. if ( ! -f "/etc/init.d/.legacy-bootordering" ) {
  34.     info("using dependency based boot sequencing");
  35.     exit insserv_updatercd(@ARGV);
  36. }
  37.  
  38. # Check out options.
  39. my $force;
  40.  
  41. my @orig_argv = @ARGV;
  42.  
  43. while($#ARGV >= 0 && ($_ = $ARGV[0]) =~ /^-/) {
  44.     shift @ARGV;
  45.     if (/^-n$/) { $notreally++; next }
  46.     if (/^-f$/) { $force++; next }
  47.     if (/^-h|--help$/) { &usage; }
  48.     &usage("unknown option");
  49. }
  50.  
  51. sub save_last_action {
  52.     my ($script, @arguments) = @_;
  53.     my $archive = "/var/lib/update-rc.d";
  54.  
  55.     return if $notreally;
  56.  
  57.     open(FILE, ">", "$archive/${script}.new") || die;
  58.     print FILE join(" ","update-rc.d",@arguments), "\n";
  59.     close(FILE);
  60.     rename "$archive/${script}.new", "$archive/${script}";
  61. }
  62.  
  63. # Action.
  64.  
  65. &usage() if ($#ARGV < 1);
  66. my $bn = shift @ARGV;
  67.  
  68. unless ($bn =~ m/[a-zA-Z0-9+.-]+/) {
  69.     print STDERR "update-rc.d: illegal character in name '$bn'\n";
  70.     exit (1);
  71. }
  72.  
  73. if ($ARGV[0] ne 'remove') {
  74.     if (! -f "$initd/$bn") {
  75.     print STDERR "update-rc.d: $initd/$bn: file does not exist\n";
  76.     exit (1);
  77.     }
  78.     &parse_lsb_header("$initd/$bn");
  79.     &cmp_args_with_defaults($bn, $ARGV[0], @ARGV);
  80. } elsif (-f "$initd/$bn") {
  81.     if (!$force) {
  82.     printf STDERR "update-rc.d: $initd/$bn exists during rc.d purge (use -f to force)\n";
  83.     exit (1);
  84.     }
  85. }
  86.  
  87. my @startlinks;
  88. my @stoplinks;
  89.  
  90. $_ = $ARGV[0];
  91. if    (/^remove$/)       { &checklinks ("remove"); save_last_action($bn, @orig_argv); }
  92. elsif (/^defaults$/)     { &defaults (@ARGV); &makelinks; save_last_action($bn, @orig_argv); }
  93. elsif (/^(start|stop)$/) { &startstop (@ARGV); &makelinks; save_last_action($bn, @orig_argv); }
  94. elsif (/^(dis|en)able$/) { &toggle (@ARGV); &makelinks; save_last_action($bn, @orig_argv); }
  95. else                     { &usage; }
  96.  
  97. exit (0);
  98.  
  99. sub info {
  100.     print STDOUT "update-rc.d: @_\n";
  101. }
  102.  
  103. sub warning {
  104.     print STDERR "update-rc.d: warning: @_\n";
  105. }
  106.  
  107. sub error {
  108.     print STDERR "update-rc.d: error: @_\n";
  109.     exit (1);
  110. }
  111.  
  112. # Check if there are links in /etc/rc[0-9S].d/ 
  113. # Remove if the first argument is "remove" and the links 
  114. # point to $bn.
  115.  
  116. sub is_link () {
  117.     my ($op, $fn, $bn) = @_;
  118.     if (! -l $fn) {
  119.     warning "$fn is not a symbolic link\n";
  120.     return 0;
  121.     } else {
  122.     my $linkdst = readlink ($fn);
  123.     if (! defined $linkdst) {
  124.         die ("update-rc.d: error reading symbolic link: $!\n");
  125.     }
  126.     if (($linkdst ne "../init.d/$bn") && ($linkdst ne "$initd/$bn")) {
  127.         warning "$fn is not a link to ../init.d/$bn or $initd/$bn\n";
  128.         return 0;
  129.     }
  130.     }
  131.     return 1;
  132. }
  133.  
  134. sub checklinks {
  135.     my ($i, $found, $fn, $islnk);
  136.  
  137.     print " Removing any system startup links for $initd/$bn ...\n"
  138.     if (defined $_[0] && $_[0] eq 'remove');
  139.  
  140.     $found = 0;
  141.  
  142.     foreach $i (0..9, 'S') {
  143.     unless (chdir ("$etcd$i.d")) {
  144.         next if ($i =~ m/^[789S]$/);
  145.         die("update-rc.d: chdir $etcd$i.d: $!\n");
  146.     }
  147.     opendir(DIR, ".");
  148.     my $saveBN=$bn;
  149.     $saveBN =~ s/\+/\\+/g;
  150.     foreach $_ (readdir(DIR)) {
  151.         next unless (/^[SK]\d\d$saveBN$/);
  152.         $fn = "$etcd$i.d/$_";
  153.         $found = 1;
  154.         $islnk = &is_link ($_[0], $fn, $bn);
  155.         next unless (defined $_[0] and $_[0] eq 'remove');
  156.         if (! $islnk) {
  157.         print "   $fn is not a link to ../init.d/$bn; not removing\n"; 
  158.         next;
  159.         }
  160.         print "   $etcd$i.d/$_\n";
  161.         next if ($notreally);
  162.         unlink ("$etcd$i.d/$_") ||
  163.         die("update-rc.d: unlink: $!\n");
  164.     }
  165.     closedir(DIR);
  166.     }
  167.     $found;
  168. }
  169.  
  170. sub parse_lsb_header {
  171.     my $initdscript = shift;
  172.     my %lsbinfo;
  173.     my $lsbheaders = "Provides|Required-Start|Required-Stop|Default-Start|Default-Stop";
  174.     open(INIT, "<$initdscript") || die "error: unable to read $initdscript";
  175.     while (<INIT>) {
  176.         chomp;
  177.         $lsbinfo{'found'} = 1 if (m/^\#\#\# BEGIN INIT INFO\s*$/);
  178.         last if (m/\#\#\# END INIT INFO\s*$/);
  179.         if (m/^\# ($lsbheaders):\s*(\S?.*)$/i) {
  180.         $lsbinfo{lc($1)} = $2;
  181.         }
  182.     }
  183.     close(INIT);
  184.  
  185.     # Check that all the required headers are present
  186.     if (!$lsbinfo{found}) {
  187.     printf STDERR "update-rc.d: warning: $initdscript missing LSB information\n";
  188.     printf STDERR "update-rc.d: see <http://wiki.debian.org/LSBInitScripts>\n";
  189.     } else {
  190.         for my $key (split(/\|/, lc($lsbheaders))) {
  191.             if (!exists $lsbinfo{$key}) {
  192.                 warning "$initdscript missing LSB keyword '$key'\n";
  193.             }
  194.         }
  195.     }
  196. }
  197.  
  198.  
  199. # Process the arguments after the "enable" or "disable" keyword.
  200.  
  201. sub toggle {
  202.     my @argv = @_;
  203.     my ($action, %lvls, @start, @stop, @xstartlinks);
  204.  
  205.     if (!&checklinks) {
  206.     print " System start/stop links for $initd/$bn do not exist.\n";
  207.     exit (0);
  208.     }
  209.  
  210.     $action = $argv[0];
  211.     if ($#argv > 1) {
  212.     while ($#argv > 0 && shift @argv) {
  213.         if ($argv[0] =~ /^[S2-5]$/) {
  214.         $lvls{$argv[0]}++;
  215.         } else {
  216.         &usage ("expected 'S' '2' '3' '4' or '5'");
  217.         }
  218.     }
  219.     } else {
  220.     $lvls{$_}++ for ('S', '2', '3', '4', '5');
  221.     }
  222.  
  223.     push(@start, glob($etcd . '[2-5S].d/[KS][0-9][0-9]' . $bn));
  224.  
  225.     foreach (@start) {
  226.     my $islink = &is_link (undef, $_, $bn);
  227.     next if !$islink;
  228.  
  229.     next unless my ($lvl, $sk, $seq) = m/^$etcd([2-5S])\.d\/([SK])([0-9]{2})$bn$/;
  230.     $startlinks[$lvl] = $sk . $seq;
  231.  
  232.     if ($action eq 'disable' and $sk eq 'S' and $lvls{$lvl}) {
  233.         $xstartlinks[$lvl] = 'K' . sprintf "%02d", (100 - $seq);
  234.     } elsif ($action eq 'enable' and $sk eq 'K' and $lvls{$lvl}) {
  235.         $xstartlinks[$lvl] = 'S' . sprintf "%02d", -($seq - 100);
  236.     } else {
  237.         $xstartlinks[$lvl] = $sk . $seq;
  238.     }
  239.     }
  240.  
  241.     push(@stop, glob($etcd . '[016].d/[KS][0-9][0-9]' . $bn));
  242.  
  243.     foreach (@stop) {
  244.     my $islink = &is_link (undef, $_, $bn);
  245.     next if !$islink;
  246.  
  247.     next unless my ($lvl, $sk, $seq) = m/^$etcd([016])\.d\/([SK])([0-9]{2})$bn$/;
  248.     $stoplinks[$lvl] = $sk . $seq;
  249.     }
  250.  
  251.     if ($action eq 'disable') {
  252.     print " Disabling system startup links for $initd/$bn ...\n";
  253.     } elsif ($action eq 'enable') {
  254.     print " Enabling system startup links for $initd/$bn ...\n";
  255.     }
  256.  
  257.     &checklinks ("remove");
  258.     @startlinks = @xstartlinks;
  259.  
  260.     1;
  261. }
  262.  
  263. # Process the arguments after the "defaults" keyword.
  264.  
  265. sub defaults {
  266.     my @argv = @_;
  267.     my ($start, $stop) = (20, 20);
  268.  
  269.     &usage ("defaults takes only one or two codenumbers") if ($#argv > 2);
  270.     $start = $stop = $argv[1] if ($#argv >= 1);
  271.     $stop  =         $argv[2] if ($#argv >= 2);
  272.     &usage ("codenumber must be a number between 0 and 99")
  273.     if ($start !~ /^\d\d?$/ || $stop  !~ /^\d\d?$/);
  274.  
  275.     $start = sprintf("%02d", $start);
  276.     $stop  = sprintf("%02d", $stop);
  277.  
  278.     $stoplinks[$_]  = "K$stop"  for (0, 1, 6);
  279.     $startlinks[$_] = "S$start" for (2, 3, 4, 5);
  280.  
  281.     1;
  282. }
  283.  
  284. # Process the arguments after the start or stop keyword.
  285.  
  286. sub startstop {
  287.     my @argv = @_;
  288.     my($letter, $NN, $level);
  289.  
  290.     while ($#argv >= 0) {
  291.     if    ($argv[0] eq 'start') { $letter = 'S'; }
  292.     elsif ($argv[0] eq 'stop')  { $letter = 'K'; }
  293.     else {
  294.         &usage("expected start|stop");
  295.     }
  296.  
  297.     if ($argv[1] !~ /^\d\d?$/) {
  298.         &usage("expected NN after $argv[0]");
  299.     }
  300.     $NN = sprintf("%02d", $argv[1]);
  301.  
  302.     if ($argv[-1] ne '.') {
  303.         &usage("start|stop arguments not terminated by \".\"");
  304.     }
  305.  
  306.     shift @argv; shift @argv;
  307.     $level = shift @argv;
  308.     do {
  309.         if ($level !~ m/^[0-9S]$/) {
  310.         &usage(
  311.                "expected runlevel [0-9S] (did you forget \".\" ?)");
  312.         }
  313.         if (! -d "$etcd$level.d") {
  314.         print STDERR
  315.             "update-rc.d: $etcd$level.d: no such directory\n";
  316.         exit(1);
  317.         }
  318.         $level = 99 if ($level eq 'S');
  319.         $startlinks[$level] = "$letter$NN" if ($letter eq 'S');
  320.         $stoplinks[$level]  = "$letter$NN" if ($letter eq 'K');
  321.     } while (($level = shift @argv) ne '.');
  322.     }
  323.     1;
  324. }
  325.  
  326. # Create the links.
  327.  
  328. sub makelinks {
  329.     my($t, $i);
  330.     my @links;
  331.  
  332.     if (&checklinks) {
  333.     print " System start/stop links for $initd/$bn already exist.\n";
  334.     return 0;
  335.     }
  336.     print " Adding system startup for $initd/$bn ...\n";
  337.  
  338.     # nice unreadable perl mess :)
  339.  
  340.     for($t = 0; $t < 2; $t++) {
  341.     @links = $t ? @startlinks : @stoplinks;
  342.     for($i = 0; $i <= $#links; $i++) {
  343.         my $lvl = $i;
  344.         $lvl = 'S' if ($i == 99);
  345.         next if (!defined $links[$i] or $links[$i] eq '');
  346.         print "   $etcd$lvl.d/$links[$i]$bn -> ../init.d/$bn\n";
  347.         next if ($notreally);
  348.         symlink("../init.d/$bn", "$etcd$lvl.d/$links[$i]$bn")
  349.         || die("update-rc.d: symlink: $!\n");
  350.     }
  351.     }
  352.  
  353.     1;
  354. }
  355.  
  356. ## Dependency based
  357. sub insserv_updatercd {
  358.     my @args = @_;
  359.     my @opts;
  360.     my $scriptname;
  361.     my $action;
  362.     my $notreally = 0;
  363.  
  364.     my @orig_argv = @args;
  365.  
  366.     while($#args >= 0 && ($_ = $args[0]) =~ /^-/) {
  367.         shift @args;
  368.         if (/^-n$/) { push(@opts, $_); $notreally++; next }
  369.         if (/^-f$/) { push(@opts, $_); next }
  370.         if (/^-h|--help$/) { &usage; }
  371.         usage("unknown option");
  372.     }
  373.  
  374.     usage("not enough arguments") if ($#args < 1);
  375.  
  376.     $scriptname = shift @args;
  377.     $action = shift @args;
  378.     if ("remove" eq $action) {
  379.         if ( -f "/etc/init.d/$scriptname" ) {
  380.             my $rc = system "insserv", @opts, "-r", $scriptname;
  381.             if (0 == $rc && !$notreally) {
  382.                 save_last_action($scriptname, @orig_argv);
  383.             }
  384.             exit $rc;
  385.         } else {
  386.             # insserv removes all dangling symlinks, no need to tell it
  387.             # what to look for.
  388.             my $rc = system "insserv", @opts;
  389.             if (0 == $rc && !$notreally) {
  390.                 save_last_action($scriptname, @orig_argv);
  391.             }
  392.             exit $rc;
  393.         }
  394.     } elsif ("defaults" eq $action || "start" eq $action ||
  395.              "stop" eq $action) {
  396.         # All start/stop/defaults arguments are discarded so emit a
  397.         # message if arguments have been given and are in conflict
  398.         # with Default-Start/Default-Stop values of LSB comment.
  399.         cmp_args_with_defaults($scriptname, $action, @args);
  400.  
  401.         if ( -f "/etc/init.d/$scriptname" ) {
  402.             my $rc = system "insserv", @opts, $scriptname;
  403.             if (0 == $rc && !$notreally) {
  404.                 save_last_action($scriptname, @orig_argv);
  405.             }
  406.             exit $rc;
  407.         } else {
  408.             error("initscript does not exist: /etc/init.d/$scriptname");
  409.         }
  410.     } elsif ("disable" eq $action || "enable" eq $action) {
  411.         insserv_toggle($notreally, $action, $scriptname, @args);
  412.         # Call insserv to resequence modified links
  413.         my $rc = system "insserv", @opts, $scriptname;
  414.         if (0 == $rc && !$notreally) {
  415.             save_last_action($scriptname, @orig_argv);
  416.         }
  417.         exit $rc;
  418.     } else {
  419.         usage();
  420.     }
  421. }
  422.  
  423. sub parse_def_start_stop {
  424.     my $script = shift;
  425.     my (%lsb, @def_start_lvls, @def_stop_lvls);
  426.  
  427.     open my $fh, '<', $script or error("unable to read $script");
  428.     while (<$fh>) {
  429.         chomp;
  430.         if (m/^### BEGIN INIT INFO$/) {
  431.             $lsb{'begin'}++;
  432.         }
  433.         elsif (m/^### END INIT INFO$/) {
  434.             $lsb{'end'}++;
  435.             last;
  436.         }
  437.         elsif ($lsb{'begin'} and not $lsb{'end'}) {
  438.             if (m/^# Default-Start:\s*(\S?.*)$/) {
  439.                 @def_start_lvls = split(' ', $1);
  440.             }
  441.             if (m/^# Default-Stop:\s*(\S?.*)$/) {
  442.                 @def_stop_lvls = split(' ', $1);
  443.             }
  444.         }
  445.     }
  446.     close($fh);
  447.  
  448.     return (\@def_start_lvls, \@def_stop_lvls);
  449. }
  450.  
  451. sub lsb_header_for_script {
  452.     my $name = shift;
  453.  
  454.     foreach my $file ("/etc/insserv/overrides/$name", "/etc/init.d/$name",
  455.                       "/usr/share/insserv/overrides/$name") {
  456.         return $file if -s $file;
  457.     }
  458.  
  459.     error("cannot find a LSB script for $name");
  460. }
  461.  
  462. sub cmp_args_with_defaults {
  463.     my ($name, $act) = (shift, shift);
  464.     my ($lsb_start_ref, $lsb_stop_ref, $arg_str, $lsb_str);
  465.     my (@arg_start_lvls, @arg_stop_lvls, @lsb_start_lvls, @lsb_stop_lvls);
  466.  
  467.     ($lsb_start_ref, $lsb_stop_ref) = parse_def_start_stop("/etc/init.d/$name");
  468.     @lsb_start_lvls = @$lsb_start_ref;
  469.     @lsb_stop_lvls  = @$lsb_stop_ref;
  470.     return if (!@lsb_start_lvls and !@lsb_stop_lvls);
  471.  
  472.     if ($act eq 'defaults') {
  473.         @arg_start_lvls = (2, 3, 4, 5);
  474.         @arg_stop_lvls  = (0, 1, 6);
  475.     } elsif ($act eq 'start' or $act eq 'stop') {
  476.         my $start = $act eq 'start' ? 1 : 0;
  477.         my $stop = $act eq 'stop' ? 1 : 0;
  478.  
  479.         # The legacy part of this program passes arguments starting with
  480.         # "start|stop NN x y z ." but the insserv part gives argument list
  481.         # starting with sequence number (ie. strips off leading "start|stop")
  482.         # Start processing arguments immediately after the first seq number.
  483.         my $argi = $_[0] eq $act ? 2 : 1;
  484.  
  485.         while (defined $_[$argi]) {
  486.             my $arg = $_[$argi];
  487.  
  488.             # Runlevels 0 and 6 are always stop runlevels
  489.             if ($arg eq 0 or $arg eq 6) {
  490.         $start = 0; $stop = 1; 
  491.             } elsif ($arg eq 'start') {
  492.                 $start = 1; $stop = 0; $argi++; next;
  493.             } elsif ($arg eq 'stop') {
  494.                 $start = 0; $stop = 1; $argi++; next;
  495.             } elsif ($arg eq '.') {
  496.                 next;
  497.             }
  498.             push(@arg_start_lvls, $arg) if $start;
  499.             push(@arg_stop_lvls, $arg) if $stop;
  500.         } continue {
  501.             $argi++;
  502.         }
  503.     }
  504.  
  505.     if ($#arg_start_lvls != $#lsb_start_lvls or
  506.         join("\0", sort @arg_start_lvls) ne join("\0", sort @lsb_start_lvls)) {
  507.         $arg_str = @arg_start_lvls ? "@arg_start_lvls" : "none";
  508.         $lsb_str = @lsb_start_lvls ? "@lsb_start_lvls" : "none";
  509.         warning "$name start runlevel arguments ($arg_str) do not match",
  510.                 "LSB Default-Start values ($lsb_str)";
  511.     }
  512.     if ($#arg_stop_lvls != $#lsb_stop_lvls or
  513.         join("\0", sort @arg_stop_lvls) ne join("\0", sort @lsb_stop_lvls)) {
  514.         $arg_str = @arg_stop_lvls ? "@arg_stop_lvls" : "none";
  515.         $lsb_str = @lsb_stop_lvls ? "@lsb_stop_lvls" : "none";
  516.         warning "$name stop runlevel arguments ($arg_str) do not match",
  517.                 "LSB Default-Stop values ($lsb_str)";
  518.     }
  519. }
  520.  
  521. sub insserv_toggle {
  522.     my ($dryrun, $act, $name) = (shift, shift, shift);
  523.     my (@toggle_lvls, $start_lvls, $stop_lvls, @symlinks);
  524.     my $lsb_header = lsb_header_for_script($name);
  525.  
  526.     # Extra arguments to disable|enable action are runlevels. If none
  527.     # given parse LSB info for Default-Start value.
  528.     if ($#_ >= 0) {
  529.         @toggle_lvls = @_;
  530.     } else {
  531.         ($start_lvls, $stop_lvls) = parse_def_start_stop($lsb_header);
  532.         @toggle_lvls = @$start_lvls;
  533.         if ($#toggle_lvls < 0) {
  534.             error("$name Default-Start contains no runlevels, aborting.");
  535.         }
  536.     }
  537.  
  538.     # Find symlinks in rc.d directories. Refuse to modify links in runlevels
  539.     # not used for normal system start sequence.
  540.     for my $lvl (@toggle_lvls) {
  541.         if ($lvl !~ /^[S2345]$/) {
  542.             warning("$act action will have no effect on runlevel $lvl");
  543.             next;
  544.         }
  545.         push(@symlinks, $_) for glob("/etc/rc$lvl.d/[SK][0-9][0-9]$name");
  546.     }
  547.  
  548.     if (!@symlinks) {
  549.         error("no runlevel symlinks to modify, aborting!");
  550.     }
  551.  
  552.     # Toggle S/K bit of script symlink.
  553.     for my $cur_lnk (@symlinks) {
  554.         my $sk;
  555.         my @new_lnk = split(//, $cur_lnk);
  556.  
  557.         if ("disable" eq $act) {
  558.             $sk = rindex($cur_lnk, '/S') + 1;
  559.             next if $sk < 1;
  560.             $new_lnk[$sk] = 'K';
  561.         } else {
  562.             $sk = rindex($cur_lnk, '/K') + 1;
  563.             next if $sk < 1;
  564.             $new_lnk[$sk] = 'S';
  565.         }
  566.  
  567.         if ($dryrun) {
  568.             printf("rename(%s, %s)\n", $cur_lnk, join('', @new_lnk));
  569.             next;
  570.         }
  571.  
  572.         rename($cur_lnk, join('', @new_lnk)) or error($!);
  573.     }
  574. }
  575.